use anyhow::anyhow;
use apikit::{ApiErrorItem, HttpProtocol, SseProtocol, WsProtocol};
use axum::{
    Json,
    extract::{FromRequest, FromRequestParts, Request},
    http::request::Parts,
    response::{IntoResponse, Response},
};
use carpet_base::{
    dto::{Empty, ResponseBody},
    error::ServerError,
    param::{SseResponse, StreamResult, WsResponse, WsUpgrade},
    server::error_to_response,
};
use serde::Serialize;
pub trait ConsolExtractor<E> {
    fn to_extractor(self) -> E;
}

pub struct BaseConsolExtractor<E>(pub E);

impl<E> ConsolExtractor<E> for BaseConsolExtractor<E> {
    fn to_extractor(self) -> E {
        self.0
    }
}

impl<S, E: FromRequest<S>> FromRequest<S> for BaseConsolExtractor<E>
where
    S: Send + Sync,
{
    type Rejection = E::Rejection;

    async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> {
        let inner = E::from_request(req, state).await?;
        Ok(Self(inner))
    }
}

impl<S, E: FromRequestParts<S>> FromRequestParts<S> for BaseConsolExtractor<E>
where
    S: Send + Sync,
{
    type Rejection = E::Rejection;

    async fn from_request_parts(req: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
        let inner = E::from_request_parts(req, state).await?;
        Ok(Self(inner))
    }
}

pub trait ConsolResponse<P, T> {
    fn to_response(t: T) -> Response;
}

pub struct BaseConsolResponse;

impl<T: Serialize, E> ConsolResponse<HttpProtocol, Result<T, E>> for BaseConsolResponse
where
    ApiErrorItem: From<E>,
{
    fn to_response(t: Result<T, E>) -> Response {
        match t {
            Ok(t) => Json(ResponseBody {
                code: "0".into(),
                data: t,
                message: "OK".into(),
            })
            .into_response(),
            Err(e) => error_to_response(e),
        }
    }
}

impl<E> ConsolResponse<WsProtocol, Result<WsResponse, E>> for BaseConsolResponse
where
    ApiErrorItem: From<E>,
{
    fn to_response(t: Result<WsResponse, E>) -> Response {
        match t {
            Ok(r) => {
                let inner = r.response.downcast::<Response>();
                match inner {
                    Ok(inner) => *inner,
                    Err(e) => {
                        let e = ServerError(anyhow!("{e:?}"));
                        let error_item: ApiErrorItem = e.into();
                        (
                            axum::http::StatusCode::from_u16(error_item.status).unwrap_or_default(),
                            axum::Json(ResponseBody::<Empty> {
                                code: error_item.code,
                                data: Empty,
                                message: error_item.cause,
                            }),
                        )
                            .into_response()
                    }
                }
            }
            Err(e) => error_to_response(e),
        }
    }
}

impl<T, E> ConsolResponse<SseProtocol, Result<SseResponse<T>, E>> for BaseConsolResponse
where
    ApiErrorItem: From<E>,
{
    fn to_response(t: Result<SseResponse<T>, E>) -> Response {
        match t {
            Ok(r) => {
                let inner = r.response.downcast::<Response>();
                match inner {
                    Ok(inner) => *inner,
                    Err(e) => {
                        let e = ServerError(anyhow!("{e:?}"));
                        let error_item: ApiErrorItem = e.into();
                        (
                            axum::http::StatusCode::from_u16(error_item.status).unwrap_or_default(),
                            axum::Json(ResponseBody::<Empty> {
                                code: error_item.code,
                                data: Empty,
                                message: error_item.cause,
                            }),
                        )
                            .into_response()
                    }
                }
            }
            Err(e) => error_to_response(e),
        }
    }
}

impl ConsolResponse<HttpProtocol, StreamResult> for BaseConsolResponse {
    fn to_response(t: StreamResult) -> Response {
        let inner = t.response.downcast::<Response>();
        match inner {
            Ok(inner) => *inner,
            Err(e) => {
                let e = ServerError(anyhow!("{e:?}"));
                let error_item: ApiErrorItem = e.into();
                (
                    axum::http::StatusCode::from_u16(error_item.status).unwrap_or_default(),
                    axum::Json(ResponseBody::<Empty> {
                        code: error_item.code,
                        data: Empty,
                        message: error_item.cause,
                    }),
                )
                    .into_response()
            }
        }
    }
}

pub fn origin_ws_upgrade<SendMsg, ReceiveMsg, SC, RC>(
    ws: WsUpgrade<SendMsg, ReceiveMsg, SC, RC>,
) -> Result<Box<axum::extract::ws::WebSocketUpgrade>, ServerError> {
    let ws = ws
        .inner
        .downcast::<axum::extract::ws::WebSocketUpgrade>()
        .map_err(|_| ServerError(anyhow!("Failed to downcast WebSocketUpgrade")))?;
    Ok(ws)
}
